package com.example.demo.simple.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.huaban.analysis.jieba.JiebaSegmenter;
import com.huaban.analysis.jieba.SegToken;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.zset.Tuple;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class RedisSearchService {

    // 常量定义
    public static final String DATA_PREFIX = "data:";
    public static final String INDEX_PREFIX = "idx:";
    public static final String TEMP_SCORE_PREFIX = "temp:search:score:";
    public static final int EXPIRE_TIME = 24 * 3600; // 数据过期时间(秒)

    // 评分权重配置（优化后：加大精准匹配权重）
    private static final int EXACT_MATCH_SCORE = 50;  // 完全匹配搜索词
    private static final int FULL_WORD_SCORE = 20;     // 匹配完整词
    private static final int SUB_WORD_SCORE = 5;       // 匹配子词
    private static final int SINGLE_CHAR_SCORE = 0;    // 单字匹配不计分
    private static final int MIN_SCORE_THRESHOLD = 25; // 提高最低分数阈值

    // 停用词集合
    private static final Set<String> STOP_WORDS = new HashSet<>(
            Arrays.asList("的", "了", "是", "在", "和", "呢", "呀", "啊", "哦", "电子", "智能")
    );

    private final JiebaSegmenter segmenter = new JiebaSegmenter();
    private final RedisTemplate<String, String> redisTemplate;
    private final ObjectMapper objectMapper;

    public RedisSearchService(RedisTemplate<String, String> redisTemplate, ObjectMapper objectMapper) {
        this.redisTemplate = redisTemplate;
        this.objectMapper = objectMapper;
    }

    /**
     * 保存数据并构建索引
     */
    public void save(String business, Long id, Map<String, String> fieldMap) throws JsonProcessingException {
        validateParams(business, id, fieldMap);
        saveOriginalData(business, id, fieldMap);
        buildIndex(business, id, fieldMap);
    }

    /**
     * 优化后的搜索方法：精准匹配优先 + 核心词过滤
     */
    public List<ScoredResult> searchWithScore(String business, String query, int page, int size) {
        long start = System.currentTimeMillis();
        String tempScoreKey = TEMP_SCORE_PREFIX + UUID.randomUUID();
        
        try {
            // 1. 分词并获取权重
            Map<String, Integer> tokenWeights = getTokenWeights(query);
            if (tokenWeights.isEmpty()) {
                return Collections.emptyList();
            }
            
            // 2. 提取核心词（关键优化：确保结果必须包含核心词）
            String coreToken = extractCoreToken(query);
            if (coreToken == null || coreToken.isEmpty()) {
                return Collections.emptyList();
            }

            // 3. 批量计算分数
            batchCalculateScores(business, tokenWeights, tempScoreKey);
            
            // 4. 为完全匹配的结果额外加分（大幅提高权重）
            addExactMatchScoreInRedis(business, query, tempScoreKey);
            
            // 5. 过滤：仅保留包含核心词的结果（关键优化）
            filterByCoreToken(business, coreToken, tempScoreKey);
            
            // 6. 分页查询并返回结果
            return getPagedResults(tempScoreKey, page, size);

        } finally {
            redisTemplate.expire(tempScoreKey, 10, TimeUnit.MINUTES);
            System.out.println("搜索耗时: " + (System.currentTimeMillis() - start) + "ms");
        }
    }

    /**
     * 获取原始数据
     */
    public Map<String, String> get(String business, Long id) {
        try {
            String json = redisTemplate.opsForValue().get(DATA_PREFIX + business + ":" + id);
            return json != null ? objectMapper.readValue(json, Map.class) : Collections.emptyMap();
        } catch (Exception e) {
            e.printStackTrace();
            return Collections.emptyMap();
        }
    }

    /**
     * 提取核心词（取搜索词中最关键的子词，如"苹果电子手表"的核心词是"手表"）
     */
    private String extractCoreToken(String query) {
        List<SegToken> tokens = segmenter.process(query, JiebaSegmenter.SegMode.SEARCH);
        // 倒序查找第一个长度>1且非停用词的子词（通常是最后一个有意义的词）
        for (int i = tokens.size() - 1; i >= 0; i--) {
            String word = tokens.get(i).word;
            if (word.length() > 1 && !STOP_WORDS.contains(word)) {
                return word;
            }
        }
        // 如果所有子词都不合适，使用原词作为核心词
        return query.length() > 1 ? query : null;
    }

    /**
     * 过滤：仅保留包含核心词的结果
     */
    private void filterByCoreToken(String business, String coreToken, String tempScoreKey) {
        // 1. 找到所有包含核心词的ID
        Set<String> coreIds = getIdsByToken(business, coreToken);
        if (coreIds.isEmpty()) {
            // 如果没有包含核心词的结果，清空临时分数集
            redisTemplate.delete(tempScoreKey);
            return;
        }
        
        // 2. 删除临时分数集中不包含核心词的ID
        redisTemplate.execute(new RedisCallback<Void>() {
            @Override
            public Void doInRedis(RedisConnection connection) {
                byte[] tempKeyBytes = tempScoreKey.getBytes(StandardCharsets.UTF_8);
                // 遍历临时分数集中的所有ID
                Cursor<Tuple> cursor = connection.zScan(tempKeyBytes, ScanOptions.NONE);
                while (cursor.hasNext()) {
                    byte[] idBytes = cursor.next().getValue();
                    String id = new String(idBytes, StandardCharsets.UTF_8);
                    // 如果ID不在核心词ID集合中，删除
                    if (!coreIds.contains(id)) {
                        connection.zRem(tempKeyBytes, idBytes);
                    }
                }
                return null;
            }
        });
    }

    /**
     * 根据分词获取所有匹配的ID
     */
    private Set<String> getIdsByToken(String business, String token) {
        Set<String> ids = new HashSet<>();
        String pattern = INDEX_PREFIX + business + ":*:" + token;
        Set<String> keys = scanKeys(pattern);
        for (String key : keys) {
            Set<String> keyIds = redisTemplate.opsForSet().members(key);
            if (keyIds != null) {
                ids.addAll(keyIds);
            }
        }
        return ids;
    }

    /**
     * 批量计算分数
     */
    private void batchCalculateScores(String business, Map<String, Integer> tokenWeights, String tempScoreKey) {
        // 1. 获取所有匹配的键（单独SCAN）
        Set<String> allMatchedKeys = new HashSet<>();
        for (String token : tokenWeights.keySet()) {
            String pattern = INDEX_PREFIX + business + ":*:" + token;
            allMatchedKeys.addAll(scanKeys(pattern));
        }

        if (allMatchedKeys.isEmpty()) {
            return;
        }

        // 2. 批量获取ID集合（使用String序列化器）
        Map<String, Set<String>> keyToIds = new HashMap<>();
        List<Object> results = redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) {
                for (String key : allMatchedKeys) {
                    connection.sMembers(key.getBytes(StandardCharsets.UTF_8));
                }
                return null;
            }
        }, redisTemplate.getStringSerializer());

        // 3. 映射键与ID集合的关系
        Iterator<String> keyIterator = allMatchedKeys.iterator();
        for (Object result : results) {
            if (result instanceof Set && keyIterator.hasNext()) {
                String key = keyIterator.next();
                @SuppressWarnings("unchecked")
                Set<String> ids = (Set<String>) result;
                keyToIds.put(key, ids);
            }
        }

        // 4. 批量累加分数
        redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) {
                byte[] tempScoreKeyBytes = tempScoreKey.getBytes(StandardCharsets.UTF_8);
                for (Map.Entry<String, Set<String>> entry : keyToIds.entrySet()) {
                    String key = entry.getKey();
                    String token = extractTokenFromKey(key);
                    int weight = tokenWeights.getOrDefault(token, 0);
                    if (weight <= 0) continue;

                    for (String id : entry.getValue()) {
                        connection.zIncrBy(
                            tempScoreKeyBytes,
                            weight,
                            id.getBytes(StandardCharsets.UTF_8)
                        );
                    }
                }
                return null;
            }
        });
    }

    /**
     * 为完全匹配的结果额外加分
     */
    private void addExactMatchScoreInRedis(String business, String query, String tempScoreKey) {
        String pattern = INDEX_PREFIX + business + ":*:" + query;
        Set<String> exactKeys = scanKeys(pattern);
        if (exactKeys.isEmpty()) return;

        // 批量获取ID并加分
        List<Object> results = redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) {
                for (String key : exactKeys) {
                    connection.sMembers(key.getBytes(StandardCharsets.UTF_8));
                }
                return null;
            }
        }, redisTemplate.getStringSerializer());

        // 处理结果并加分
        Iterator<String> keyIterator = exactKeys.iterator();
        redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) {
                byte[] tempKeyBytes = tempScoreKey.getBytes(StandardCharsets.UTF_8);
                for (Object result : results) {
                    if (result instanceof Set && keyIterator.hasNext()) {
                        keyIterator.next();
                        @SuppressWarnings("unchecked")
                        Set<String> ids = (Set<String>) result;
                        for (String id : ids) {
                            connection.zIncrBy(
                                tempKeyBytes,
                                EXACT_MATCH_SCORE,
                                id.getBytes(StandardCharsets.UTF_8)
                            );
                        }
                    }
                }
                return null;
            }
        });
    }

    /**
     * 安全扫描键
     */
    private Set<String> scanKeys(String pattern) {
        Set<String> keys = new HashSet<>();
        redisTemplate.execute(new RedisCallback<Void>() {
            @Override
            public Void doInRedis(RedisConnection connection) {
                ScanOptions options = ScanOptions.scanOptions().match(pattern).count(100).build();
                Cursor<byte[]> cursor = connection.scan(options);
                while (cursor.hasNext()) {
                    keys.add(new String(cursor.next(), StandardCharsets.UTF_8));
                }
                return null;
            }
        });
        return keys;
    }

    /**
     * 获取分页结果
     */
    private List<ScoredResult> getPagedResults(String tempScoreKey, int page, int size) {
        long offset = (long) page * size;
        
        Set<ZSetOperations.TypedTuple<String>> topResults = redisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(
                        tempScoreKey,
                        MIN_SCORE_THRESHOLD,
                        Double.POSITIVE_INFINITY,
                        offset,
                        size
                );

        if (topResults == null || topResults.isEmpty()) {
            return Collections.emptyList();
        }

        return topResults.stream()
                .map(tuple -> new ScoredResult(
                        Long.parseLong(tuple.getValue()),
                        tuple.getScore().intValue()
                ))
                .collect(Collectors.toList());
    }

    /**
     * 从键中提取分词
     */
    private String extractTokenFromKey(String key) {
        String[] parts = key.split(":");
        return parts.length >= 4 ? parts[3] : "";
    }

    /**
     * 获取搜索词的分词权重
     */
    private Map<String, Integer> getTokenWeights(String query) {
        Map<String, Integer> tokenWeights = new HashMap<>();

        // 1. 完整词权重
        tokenWeights.put(query, FULL_WORD_SCORE);

        // 2. 子词权重
        List<SegToken> subTokens = segmenter.process(query, JiebaSegmenter.SegMode.SEARCH);
        for (SegToken token : subTokens) {
            String word = token.word;
            if (word.length() > 1 && !word.equals(query) && !STOP_WORDS.contains(word)) {
                tokenWeights.put(word, SUB_WORD_SCORE);
            }
        }

        // 3. 单字权重（设置为0，不参与计分）
        if (query.length() > 1) {
            for (char c : query.toCharArray()) {
                String charStr = String.valueOf(c);
                if (!STOP_WORDS.contains(charStr)) {
                    tokenWeights.put(charStr, SINGLE_CHAR_SCORE);
                }
            }
        }

        return tokenWeights;
    }

    /**
     * 构建索引
     */
    private void buildIndex(String business, Long id, Map<String, String> fieldMap) {
        String idStr = id.toString();
        
        for (Map.Entry<String, String> entry : fieldMap.entrySet()) {
            String fieldName = entry.getKey();
            String fieldValue = entry.getValue();
            
            if (fieldValue == null || fieldValue.trim().isEmpty()) {
                continue;
            }

            Set<String> tokenSet = getIndexTokens(fieldValue);
            
            for (String token : tokenSet) {
                String indexKey = INDEX_PREFIX + business + ":" + fieldName + ":" + token;
                redisTemplate.opsForSet().add(indexKey, idStr);
                redisTemplate.expire(indexKey, EXPIRE_TIME, TimeUnit.SECONDS);
            }
        }
    }

    /**
     * 获取索引分词(多粒度)
     */
    private Set<String> getIndexTokens(String text) {
        Set<String> tokenSet = new HashSet<>();
        
        // 1. INDEX模式(完整词)
        List<SegToken> indexTokens = segmenter.process(text, JiebaSegmenter.SegMode.INDEX);
        indexTokens.forEach(token -> addValidToken(tokenSet, token.word));
        
        // 2. SEARCH模式(子词)
        List<SegToken> searchTokens = segmenter.process(text, JiebaSegmenter.SegMode.SEARCH);
        searchTokens.forEach(token -> addValidToken(tokenSet, token.word));
        
        return tokenSet;
    }

    /**
     * 添加有效的分词(过滤无效词)
     */
    private void addValidToken(Set<String> tokenSet, String word) {
        if (word == null || word.trim().isEmpty()) {
            return;
        }
        // 过滤停用词和单字
        if (STOP_WORDS.contains(word) || word.length() <= 1) {
            return;
        }
        tokenSet.add(word);
    }

    /**
     * 参数校验
     */
    private void validateParams(String business, Long id, Map<String, String> fieldMap) {
        if (business == null || business.trim().isEmpty()) {
            throw new IllegalArgumentException("业务类型不能为空");
        }
        if (id == null || id <= 0) {
            throw new IllegalArgumentException("ID必须为正整数");
        }
        if (fieldMap == null || fieldMap.isEmpty()) {
            throw new IllegalArgumentException("字段映射不能为空");
        }
    }

    /**
     * 保存原始数据
     */
    private void saveOriginalData(String business, Long id, Map<String, String> fieldMap) throws JsonProcessingException {
        String dataKey = DATA_PREFIX + business + ":" + id;
        String json = objectMapper.writeValueAsString(fieldMap);
        redisTemplate.opsForValue().set(dataKey, json, EXPIRE_TIME, TimeUnit.SECONDS);
    }

    /**
     * 搜索结果实体类
     */
    public static class ScoredResult {
        private final Long id;
        private final Integer score;

        public ScoredResult(Long id, Integer score) {
            this.id = id;
            this.score = score;
        }

        public Long getId() { return id; }
        public Integer getScore() { return score; }

        @Override
        public String toString() {
            return "ScoredResult{id=" + id + ", score=" + score + "}";
        }
    }
}
